#include "imgData.h"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>


line imgData::lineVertex2Line(const lineVertex &source){
	float cx,cy,dx,dy;
	line res_line;

	cx  =(source.x_end+source.x_start)*0.5f;
	cy = (source.y_start+source.y_end)*0.5f;
	dx = (source.x_end-source.x_start);
	dy = (source.y_end-source.y_start);
	// assume in plane coordinate
	float theta = atan(-dx/dy);
	if(fabs(dy)<1e-8){
		theta= PI/2;
	}

	int orient = floor(theta/PI*n_orient+0.5);
	if(orient<0)
		orient+=n_orient;

	float length = sqrt(dx*dx+dy*dy);
	//length  =floor((length/(2*h+1)-1)/2+0.5);
	length = floor(length/(4*h+2));

	res_line.length = std::max(0.0f,length);
	res_line.x = cx;
	res_line.y = cy;
	res_line.orient = orient;

	return res_line;
}


bool imgData::readDataFromMatFile(const char *file_name){
	MATFile *p_file=matOpen(file_name,"r");
	if(p_file==NULL){
		printf("error:: mat file not found: %s\n",file_name);
		printf("Will return without initialization.\n");
		return false;
	}
	
	/*mat file name*/
	mat_file_name=std::string(file_name);
	/*scalars*/
	sx = floor(0.5+matGetScalar(p_file,"sx"));
	sy = floor(0.5+matGetScalar(p_file,"sy"));

	n_orient = floor(0.5+matGetScalar(p_file,"nOrient"));
	n_length = floor(0.5+matGetScalar(p_file,"nLength"));


	l_range = floor(0.5+matGetScalar(p_file,"Lrange"));
	o_range = floor(0.5+matGetScalar(p_file,"Orange"));

	l_range2 = floor(0.5+matGetScalar(p_file,"Lrange2"));
	o_range2 = floor(0.5+matGetScalar(p_file,"Orange2"));

	h = floor(0.5+matGetScalar(p_file,"h"));
	/*source image*/
	img_r = new unsigned char[sx*sy];
	img_g = new unsigned char[sx*sy];
	img_b = new unsigned char[sx*sy];

	const unsigned char *p_img = (unsigned char*)matGetPointer(p_file,"img");
	int img_size= sx*sy;

	memcpy(img_r,&p_img[0],sizeof(unsigned char)*img_size);
	memcpy(img_g,&p_img[img_size],sizeof(unsigned char)*img_size);
	memcpy(img_b,&p_img[2*img_size],sizeof(unsigned char)*img_size);


	color_a_map = new unsigned char[sx*sy];
	color_b_map = new unsigned char[sx*sy];
	color_l_map = new unsigned char[sx*sy];
	p_img= (unsigned char*)matGetPointer(p_file,"color_a_map");
	memcpy(color_a_map,p_img,sizeof(unsigned char)*img_size);
	p_img= (unsigned char*)matGetPointer(p_file,"color_b_map");
	memcpy(color_b_map,p_img,sizeof(unsigned char)*img_size);
	p_img= (unsigned char*)matGetPointer(p_file,"color_l_map");
	memcpy(color_l_map,p_img,sizeof(unsigned char)*img_size);
	for(int i = 0; i<sx*sy; ++i){
		color_l_map[i]=(color_l_map[i]/32);
		color_a_map[i]=(color_a_map[i]/32)+8;
		color_b_map[i]=(color_b_map[i]/32)+16;
	}
	mo_map = new unsigned char[sx*sy];
	p_img= (unsigned char*)matGetPointer(p_file,"MOmap");
	memcpy(mo_map,p_img,sizeof(unsigned char)*img_size);

	//for( int i= 0; i<img_size; ++i){
	//	img_r[i]=p_img[i];
	//	img_g[i]=p_img[i+img_size];
	//	img_b[i]=p_img[i+img_size*2];
	//}
	
	gray_img = new unsigned char[sx*sy];
	p_img = (unsigned char*)matGetPointer(p_file,"gray_img");
	memcpy(gray_img,p_img,sizeof(unsigned char)*img_size);

	/*gabor maps*/
	loadCellSingle(p_file,"S1map",n_orient,img_size,S1map);
	loadCellSingle(p_file,"S2map",n_orient*n_length,img_size,S2map);
	loadCellSingle(p_file,"M2map",n_orient*n_length,img_size,M2map);
	
	
	/*C, allsymbol*/
	loadCellSingle(p_file,"C",n_orient*n_orient,(4*h+1)*(4*h+1),C);
	loadCellSingle(p_file,"allsymbol",n_orient,(2*h+1)*(2*h+1),allsymbol);

        /*release loaded pointers*/
        releaseMatMemory();
		matClose(p_file);
	return true;
}


bool imgData::releaseMatMemory(){
    while(!mem_ptr.empty()){
        mxDestroyArray(mem_ptr.back());
        mem_ptr.pop_back();
    }
    return true;
}

bool imgData::release(){


	if(color_a_map!=NULL) delete[] color_a_map;
	if(color_b_map!=NULL) delete[] color_b_map;
	if(color_l_map!=NULL) delete[] color_l_map;
	if(mo_map !=NULL) delete[] mo_map;
	if(img_r!=NULL) delete[] img_r;
	if(img_g!=NULL) delete[] img_g;
	if(img_b!=NULL) delete[] img_b;
	if(gray_img!=NULL) delete[] gray_img;
	if(S1map!=NULL) { deleteMaps(S1map,n_orient); delete[] S1map;}
	if(S2map!=NULL) { deleteMaps(S2map,n_orient*n_length); delete[] S2map;}
	if(M2map!=NULL) { deleteMaps(M2map,n_orient*n_length); delete[] M2map;}
	if(C!=NULL) { deleteMaps(C,n_orient*n_orient); delete[] C;}
	if(allsymbol!=NULL) { deleteMaps(allsymbol,n_orient); delete[] allsymbol;}


        /*release the memories hold by mxArray pointers*/
        this->releaseMatMemory();
return true;
}

void imgData::deleteMaps(float **maps, int n){
	for(int i =0; i<n;++i){
		if (maps[i]!=NULL)
			delete[] maps[i];
	}
}

/*maps should be uninitialized at all*/
bool imgData::loadCellSingle(const MATFile *p_file,const char* var_name, int n1, int n2, float** &maps){
	maps = new float*[n1];
	mxArray *p_cell=matGetVariable(const_cast<MATFile *>(p_file),var_name);
	for( int i_map=0; i_map<n1; ++i_map){
		maps[i_map]=new float[n2];
		const mxArray *p_map=mxGetCell(p_cell,i_map);
		const float *p_mem = (float*)mxGetPr(p_map);
		memcpy(maps[i_map],p_mem, sizeof(float)*n2);
	}
	this->mem_ptr.push_back(p_cell);
	return true;
}
void* imgData::matGetPointer(MATFile* p_file, const char* var_name){
        mxArray *p_array = matGetVariable(p_file,var_name);
        mem_ptr.push_back(p_array);
	return (void*)mxGetData(p_array);
}

double imgData::matGetScalar(MATFile *p_file, const char *var_name){
        mxArray *p_array = matGetVariable(p_file,var_name);
        double value=mxGetScalar(p_array);
        mxDestroyArray(p_array);
        return value;
}


lineVertex imgData::matlabLine2lineVertex(const line &source){
    lineVertex res_line_vertex;
	float half_length = (source.length*2+1)*(2*this->h+1)*0.5f;
	

	float theta = PI*source.orient/this->n_orient;
    float pre_sin = sin(theta);
    float pre_cos = cos(theta);

    res_line_vertex.x_start = source.x - half_length*pre_sin;
    res_line_vertex.y_start = source.y + half_length*pre_cos;

    res_line_vertex.x_end = source.x + half_length*pre_sin;
    res_line_vertex.y_end = source.y - half_length*pre_cos;
	
	/*convert from matlab coordinate to image coordinate*/
	int tmp = res_line_vertex.x_start;
	res_line_vertex.x_start = res_line_vertex.y_start -1- this->sy*0.5f;
	res_line_vertex.y_start = tmp -1 - this->sx*0.5f;
	
	tmp = res_line_vertex.x_end;
	res_line_vertex.x_end = res_line_vertex.y_end-1- this->sy*0.5f;;
	res_line_vertex.y_end = tmp-1 - this->sx*0.5f;

    return res_line_vertex;
}
